# First example of PyQt programming.
# Robert Bridson
# Released under the GPLv3

# Get the Python Qt bindings (PyQt4 or PySide)
from PyQt4.QtCore import *
from PyQt4.QtGui import *
# Also get the bridge from Qt to OpenGL
from PyQt4.QtOpenGL import *
# OpenGL itself of course
import OpenGL
from OpenGL.GL import *
# Need numpy for data arrays
from numpy import array, zeros
# need some basic math
import math
# And basic operating system stuff (to quit)
import sys

#########################################################################
# vertex shader
vshader_src = """
    #version 110

    varying vec3 paramCoord;

    void main()
    {
        gl_Position = gl_Vertex; //+ vec4(0.05*noise3(5.0*gl_Vertex.xyz), 1.0);
        paramCoord = gl_Vertex.xyz; // + 0.2 * noise3(5.0*gl_Vertex.xyz);

        gl_FrontColor = vec4(0.5+0.5*paramCoord.x, 
                             0.5+0.5*paramCoord.y, 
                             0.5+0.5*paramCoord.z, 1.0);

        /*
        vec3 L = normalize(vec3(5, 7, 6));
        vec3 N = normalize(paramCoord);
        float s = dot(L, N);
        float g = pow(s, 10.0);
        gl_FrontColor = vec4(g, g, g, 1);
        */

        gl_BackColor = vec4(1.0, 0.0, 0.0, 1.0); // red to show up backfaces
    }
    """

# fragment shader
fshader_src = """
    #version 110

    varying vec3 paramCoord;

    void main()
    {
        gl_FragColor = gl_Color;

        vec3 L = normalize(vec3(5, 7, 6));
        vec3 N = normalize(paramCoord + 0.2 * noise3(5.0*paramCoord));
        float s = dot(L, N);
        float g = pow(s, 10.0);
        gl_FragColor = vec4(g, g, g, 1) + 0.2*gl_Color;
    }
    """
#########################################################################


# Basic GL window to play with
class MyGL(QGLWidget):
    def __init__(self, parent):
        QGLWidget.__init__(self, parent)
        self.setMinimumSize(QSize(400,400))

    def initializeGL(self):
        glClearColor(0.0, 0.0, 0.0, 0.0)
        glClearDepth(1)

        # set up shaders
        vshader_id = glCreateShader(GL_VERTEX_SHADER)
        glShaderSource(vshader_id, vshader_src)
        glCompileShader(vshader_id)
        print("vertex shader compile status: "+
                str(glGetShaderiv(vshader_id, GL_COMPILE_STATUS)))
        print(glGetShaderInfoLog(vshader_id))

        fshader_id = glCreateShader(GL_FRAGMENT_SHADER)
        glShaderSource(fshader_id, fshader_src)
        glCompileShader(fshader_id)
        print("fragment shader compile status: "+
                str(glGetShaderiv(fshader_id, GL_COMPILE_STATUS)))
        print(glGetShaderInfoLog(fshader_id))

        self.shader = glCreateProgram()
        glAttachShader(self.shader, vshader_id)
        glAttachShader(self.shader, fshader_id)
        glLinkProgram(self.shader)
        print("shader program link status: "+
                str(glGetProgramiv(self.shader, GL_LINK_STATUS)))
        if glGetProgramiv(self.shader, GL_LINK_STATUS) == 0:
            sys.exit()

        # set up vertex and element buffer for a simple mesh (sphere-ish)
        self.meshbuffers = glGenBuffers(2)
        glBindBuffer(GL_ARRAY_BUFFER, self.meshbuffers[0])
        ni = 25
        nj = 25
        self.vertex = zeros(ni*nj*4, dtype='float32')
        for i in xrange(ni):
            theta = 2*math.pi*i/float(ni-1)
            for j in xrange(nj):
                phi = math.pi*(j+0.5)/float(nj)
                self.vertex[4*(i+ni*j)] = math.sin(phi)*math.cos(theta)
                self.vertex[4*(i+ni*j)+1] = math.cos(phi)
                self.vertex[4*(i+ni*j)+2] = math.sin(phi)*math.sin(theta)
                self.vertex[4*(i+ni*j)+3] = 1
        glBufferData(GL_ARRAY_BUFFER, self.vertex, GL_STATIC_DRAW)
        glVertexPointer(4, GL_FLOAT, 0, None)

        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.meshbuffers[1])
        self.triangle = zeros(6*(ni-1)*(nj-1), dtype='uint32')
        for i in xrange(ni-1):
            for j in xrange(nj-1):
                self.triangle[6*(i+(ni-1)*j)] = i+ni*j
                self.triangle[6*(i+(ni-1)*j)+1] = (i+1)+ni*j
                self.triangle[6*(i+(ni-1)*j)+2] = (i+1)+ni*(j+1)
                self.triangle[6*(i+(ni-1)*j)+3] = i+ni*j
                self.triangle[6*(i+(ni-1)*j)+4] = (i+1)+ni*(j+1)
                self.triangle[6*(i+(ni-1)*j)+5] = i+ni*(j+1)
        glBufferData(GL_ELEMENT_ARRAY_BUFFER, self.triangle, GL_STATIC_DRAW)

    def resizeGL(self, w, h):
        glViewport(0, 0, w, h) 

    def paintGL(self):
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
        glUseProgram(self.shader)
        glEnableClientState(GL_VERTEX_ARRAY)
        glBindBuffer(GL_ARRAY_BUFFER, self.meshbuffers[0])
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, self.meshbuffers[1])
        glDrawElements(GL_TRIANGLES, len(self.triangle), GL_UNSIGNED_INT, None)

# Execute
if __name__ == "__main__":
    # Create the (single) Qt Application object, which starts everything up
    app = QApplication(sys.argv)
    # Get our main window
    gui = MyGL(None)
    gui.resize(512, 512)
    gui.show()
    gui.setFocus()
    # Run it
    app.exec_()
    sys.exit()
